package dk.mirasola.systemtraining.user.server.service;

import com.google.appengine.api.datastore.Email;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.ObjectifyService;
import dk.mirasola.systemtraining.biddingsession.server.SessionServiceImpl;
import dk.mirasola.systemtraining.bridgewidgets.shared.model.BiddingSequence;
import dk.mirasola.systemtraining.bridgewidgets.shared.model.Distribution;
import dk.mirasola.systemtraining.bridgewidgets.shared.model.Seat;
import dk.mirasola.systemtraining.server.MailUtil;
import dk.mirasola.systemtraining.server.ObjectifyUtil;
import dk.mirasola.systemtraining.server.entity.AutoBid;
import dk.mirasola.systemtraining.server.entity.BiddingSession;
import dk.mirasola.systemtraining.server.entity.BiddingSet;
import dk.mirasola.systemtraining.server.entity.Board;
import dk.mirasola.systemtraining.server.i18n.MailTexts;
import dk.mirasola.systemtraining.server.i18n.TextsFactory;
import dk.mirasola.systemtraining.user.shared.service.BiddingSetDataProviderService;
import dk.mirasola.systemtraining.user.shared.transfer.BiddingSetDetailsTO;
import dk.mirasola.systemtraining.user.shared.transfer.CreateBiddingSetTO;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

public class BiddingSetDataProviderServiceImpl extends RemoteServiceServlet implements BiddingSetDataProviderService {
    private static final Logger logger = Logger.getLogger(BiddingSetDataProviderServiceImpl.class.getSimpleName());
    private static final int MAX_BIDDINGS_SET = 20;

    public static void deleteBiddingSetChildren(Key<BiddingSet> biddingSetKey, Objectify ofy) {
        // boards
        ofy.delete(ofy.query(Board.class)
           .ancestor(biddingSetKey)
           .fetchKeys());

        // bidding session
        ofy.delete(ofy.query(BiddingSession.class)
           .ancestor(biddingSetKey)
           .fetchKeys());
    }

    public void createBiddingSetAndFirstBiddingSession(final CreateBiddingSetTO createBiddingSetTO) {
        final User currentUser = UserServiceFactory.getUserService().getCurrentUser();

        if (countBiddingSets(ObjectifyService.begin(),currentUser) >= MAX_BIDDINGS_SET) {
            logger.warning("User " + currentUser.getEmail() + " tried to create more than allowed maximum bidding sets");
            throw new RuntimeException("Maximum bidding sets for user "+currentUser.getEmail()+" hit");
        }

        ObjectifyUtil.doTransactional(new ObjectifyUtil.ObjectifyCallback<Void>() {
            @Override
            public Void doInTransaction(Objectify ofy) {

                // create biddingSet
                BiddingSet biddingSet = new BiddingSet();
                biddingSet.setOwner(currentUser);
                biddingSet.setName(createBiddingSetTO.getName());
                biddingSet.setDescription(createBiddingSetTO.getDescription());
                biddingSet.setDistributionFilterGroup(createBiddingSetTO.getDistributionFilter());
                biddingSet.setBoardsCount(createBiddingSetTO.getDistributions().size());
                biddingSet.setState(BiddingSet.State.BIDDING_IN_PROGRESS);
                biddingSet.setSeatInTurn(Seat.NORTH);
                biddingSet.setNorthLocale(createBiddingSetTO.getNorthLocale());
                biddingSet.setSouthLocale(createBiddingSetTO.getSouthLocale());
                biddingSet.setCreated(new Date());
                if (createBiddingSetTO.getNorthEmail() == null) {
                    biddingSet.setNorthEmail(new Email(currentUser.getEmail()));
                } else {
                    biddingSet.setNorthEmail(new Email(createBiddingSetTO.getNorthEmail()));
                }

                if (createBiddingSetTO.getSouthEmail() == null) {
                    biddingSet.setSouthEmail(new Email(currentUser.getEmail()));
                } else {
                    biddingSet.setSouthEmail(new Email(createBiddingSetTO.getSouthEmail()));
                }
                ofy.put(biddingSet);

                // create boards and first session
                int boardNumber = 1;
                Key<BiddingSet> biddingSetKey = ofy.getFactory().getKey(biddingSet);
                List<Board> boards = new ArrayList<Board>(createBiddingSetTO.getDistributions().size());
                for (Distribution distribution : createBiddingSetTO.getDistributions()) {
                    Board board = new Board(boardNumber, biddingSetKey);
                    board.setDistribution(distribution);
                    board.setBiddingSequence(new BiddingSequence(Seat.NORTH));
                    board.setComments(new ArrayList<String>());
                    boards.add(board);
                    boardNumber++;
                }

                ofy.put(boards);

                // create session and
                BiddingSession biddingSession = new BiddingSession(biddingSetKey);
                ofy.put(biddingSession);

                // todo autobids
                List<AutoBid> autoBids = new ArrayList<AutoBid>();

                SessionServiceImpl.autoBidForNorthOrSouthAndSendInTurnNotification(biddingSet, autoBids, boards, biddingSession, ofy);
                ofy.put(biddingSet);
                return null;
            }
        });
    }

    public List<BiddingSetDetailsTO> getBiddingSetDetails(final int offset, final int limit) {
        Objectify ofy = ObjectifyService.begin();

        List<BiddingSet> biddingSets =
                ofy.query(BiddingSet.class)
                   .filter("owner", UserServiceFactory.getUserService().getCurrentUser())
                   .order("-id")
                   .offset(offset)
                   .limit(limit)
                   .list();

        List<BiddingSetDetailsTO> result = new ArrayList<BiddingSetDetailsTO>();
        for (BiddingSet biddingSet : biddingSets) {
            result.add(createBiddingSetDetailsTO(biddingSet, ofy.getFactory()));
        }
        return result;

    }

    @Override
    public int[] initialData() {
        return new int[] {countBiddingSets(ObjectifyService.begin(),UserServiceFactory.getUserService().getCurrentUser()),MAX_BIDDINGS_SET};
    }

    private int countBiddingSets(Objectify ofy, User user) {
        return ofy.query(BiddingSet.class)
                  .filter("owner", user)
                  .count();
    }

    @Override
    public void deleteBiddingSets(final Set<String> biddingSetKeys) {
        if (biddingSetKeys == null || biddingSetKeys.size() == 0) {
            return;
        }

        for (final String biddingSetKeyStr : biddingSetKeys) {
            ObjectifyUtil.doTransactional(new ObjectifyUtil.ObjectifyCallback<Void>() {
                @Override
                public Void doInTransaction(Objectify ofy) {
                    Key<BiddingSet> biddingSetKey = ofy.getFactory().stringToKey(biddingSetKeyStr);
                    deleteBiddingSetChildren(biddingSetKey, ofy);
                    ofy.delete(biddingSetKey);
                    return null;
                }
            });
        }
    }

    private BiddingSetDetailsTO createBiddingSetDetailsTO(BiddingSet biddingSet, ObjectifyFactory factory) {
        BiddingSetDetailsTO to = new BiddingSetDetailsTO();
        to.setId(factory.keyToString(factory.getKey(biddingSet)));
        to.setName(biddingSet.getName());
        to.setDescription(biddingSet.getDescription());
        to.setBoardsCount(biddingSet.getBoardsCount());
        to.setCompleted(biddingSet.getState().equals(BiddingSet.State.COMPLETED));
        to.setInTurn(biddingSet.getSeatInTurn());
        to.setNorthEmail(biddingSet.getNorthEmail().getEmail());
        to.setSouthEmail(biddingSet.getSouthEmail().getEmail());
        to.setCreated(biddingSet.getCreated());
        return to;
    }
}

